import base64
import hashlib
import hmac
import json
import os
import logging
from datetime import datetime
from time import mktime
from urllib.parse import urlencode
from wsgiref.handlers import format_date_time
from flask import Flask, request, jsonify
from flask_cors import CORS
import requests

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[logging.FileHandler("voiceprint.log"), logging.StreamHandler()]
)
logger = logging.getLogger(__name__)

app = Flask(__name__)
CORS(app)  # 解决跨域问题


# 配置管理
class Config:
    def __init__(self):
        self.app_id = os.getenv('APP_ID', '84b3457f')  # 替换为实际值
        self.api_key = os.getenv('API_KEY', '02540812ce357bed3742c6664414df9b')
        self.api_secret = os.getenv('API_SECRET', 'YWQwMDJlZGE4Y2ZlOTNiZTc4NjdmZTdl')
        self.default_group_id = "my_group_001"
        self.default_threshold = 0.8
        self.base_url = 'https://api.xf-yun.com/v1/private/s782b4996'


config = Config()


# 签名生成类（保持原有逻辑）
class GenReqUrl:
    def parse_url(self, request_url):
        try:
            stidx = request_url.index("://")
            host = request_url[stidx + 3:]
            edidx = host.index("/")
            if edidx <= 0:
                raise ValueError(f"无效的请求URL: {request_url}")
            self.path = host[edidx:]
            self.host = host[:edidx]
        except ValueError as e:
            logger.error(f"URL解析失败: {e}")
            raise

    def assemble_ws_auth_url(self, request_url, api_key, api_secret, method="POST"):
        self.parse_url(request_url)
        now = datetime.now()
        date = format_date_time(mktime(now.timetuple()))

        signature_origin = f"host: {self.host}\ndate: {date}\n{method} {self.path} HTTP/1.1"
        signature_sha = hmac.new(
            api_secret.encode('utf-8'),
            signature_origin.encode('utf-8'),
            digestmod=hashlib.sha256
        ).digest()
        signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')

        authorization_origin = (
            f'api_key="{api_key}", algorithm="hmac-sha256", '
            f'headers="host date request-line", signature="{signature_sha}"'
        )
        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')

        values = {"host": self.host, "date": date, "authorization": authorization}
        return request_url + "?" + urlencode(values)


# 声纹识别核心类
class VoiceprintService:
    def __init__(self):
        self.url_generator = GenReqUrl()

    def send_request(self, api_name, file_bytes=None, feature_id=None, group_id=None):
        request_url = self.url_generator.assemble_ws_auth_url(
            request_url=config.base_url,
            api_key=config.api_key,
            api_secret=config.api_secret
        )

        request_body = self._generate_request_body(
            api_name=api_name,
            file_bytes=file_bytes,
            feature_id=feature_id,
            group_id=group_id or config.default_group_id
        )

        headers = {'Content-Type': 'application/json', 'Host': 'api.xf-yun.com'}
        try:
            response = requests.post(
                url=request_url,
                headers=headers,
                data=json.dumps(request_body),
                timeout=30
            )
            if response.status_code != 200:
                raise Exception(f"请求失败: {response.text}")
            return response.json()
        except Exception as e:
            logger.error(f"请求错误: {str(e)}")
            raise

    def _generate_request_body(self, api_name, file_bytes=None, feature_id=None, group_id=None):
        base_body = {
            "header": {"app_id": config.app_id, "status": 3},
            "parameter": {"s782b4996": {"groupId": group_id}}
        }

        if api_name == "createGroup":
            base_body["parameter"]["s782b4996"].update({
                "func": "createGroup",
                "groupName": "声纹识别测试组",
                "groupInfo": "用于Web界面测试",
                "createGroupRes": {"encoding": "utf8", "compress": "raw", "format": "json"}
            })
            return base_body

        elif api_name == "createFeature":
            if not file_bytes or not feature_id:
                raise ValueError("缺少音频文件或用户ID")
            base_body["parameter"]["s782b4996"].update({
                "func": "createFeature",
                "featureId": feature_id,
                "featureInfo": f"用户{feature_id}的声纹特征",
                "createFeatureRes": {"encoding": "utf8", "compress": "raw", "format": "json"}
            })
            base_body["payload"] = {
                "resource": {
                    "encoding": "lame",
                    "sample_rate": 16000,
                    "channels": 1,
                    "bit_depth": 16,
                    "status": 3,
                    "audio": base64.b64encode(file_bytes).decode('UTF-8')
                }
            }
            return base_body

        elif api_name == "searchScoreFea":
            if not file_bytes or not feature_id:
                raise ValueError("缺少音频文件或用户ID")
            base_body["parameter"]["s782b4996"].update({
                "func": "searchScoreFea",
                "dstFeatureId": feature_id,
                "searchScoreFeaRes": {"encoding": "utf8", "compress": "raw", "format": "json"}
            })
            base_body["payload"] = {
                "resource": {
                    "encoding": "lame",
                    "sample_rate": 16000,
                    "channels": 1,
                    "bit_depth": 16,
                    "status": 3,
                    "audio": base64.b64encode(file_bytes).decode('UTF-8')
                }
            }
            return base_body

        else:
            raise ValueError(f"不支持的API: {api_name}")

    def decode_response(self, response, field_name):
        try:
            encoded_text = response.get("payload", {}).get(field_name, {}).get("text", "")
            return json.loads(base64.b64decode(encoded_text).decode('utf-8')) if encoded_text else None
        except Exception as e:
            logger.error(f"响应解码失败: {str(e)}")
            return None


# 初始化服务
voiceprint_service = VoiceprintService()


# Flask路由
@app.route('/api/create-group', methods=['POST'])
def create_group():
    try:
        data = request.json
        group_id = data.get('group_id', config.default_group_id)
        response = voiceprint_service.send_request("createGroup", group_id=group_id)

        if response["header"]["code"] == 0:
            return jsonify({"status": "success", "message": f"分组 {group_id} 创建成功"})
        elif response["header"]["code"] == 23006:
            return jsonify({"status": "warning", "message": f"分组 {group_id} 已存在"})
        else:
            return jsonify({"status": "error", "message": response["header"]["message"]})
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500


@app.route('/api/register-feature', methods=['POST'])
def register_feature():
    try:
        feature_id = request.form.get('feature_id')
        group_id = request.form.get('group_id', config.default_group_id)
        audio_file = request.files.get('audio')

        if not audio_file or not feature_id:
            return jsonify({"status": "error", "message": "请提供音频文件和用户ID"}), 400

        file_bytes = audio_file.read()
        response = voiceprint_service.send_request(
            "createFeature",
            file_bytes=file_bytes,
            feature_id=feature_id,
            group_id=group_id
        )

        if response["header"]["code"] == 0:
            return jsonify({"status": "success", "message": f"用户 {feature_id} 声纹注册成功"})
        else:
            return jsonify({"status": "error", "message": response["header"]["message"]})
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500


# 在VoiceprintService类中添加验证方法
def verify_voiceprint(self, audio_file, feature_id, group_id):
    """验证声纹特征"""
    try:
        # 读取音频文件内容
        file_bytes = audio_file.read()
        
        # 调用声纹比对API
        response = self.send_request(
            api_name="searchScoreFea",
            file_bytes=file_bytes,
            feature_id=feature_id,
            group_id=group_id
        )
        
        # 解析响应结果
        result = self.decode_response(response, "searchScoreFeaRes")
        
        if not result or "score" not in result:
            return {"status": "error", "message": "声纹验证失败: 无效响应"}
        
        # 判断是否通过验证
        is_verified = result["score"] >= config.default_threshold
        return {
            "status": "success",
            "verified": is_verified,
            "score": result["score"],
            "threshold": config.default_threshold
        }
    except Exception as e:
        logger.error(f"声纹验证异常: {str(e)}")
        return {"status": "error", "message": str(e)}

# 添加Flask验证端点
@app.route('/api/verify-feature', methods=['POST'])
def verify_feature():
    try:
        if not request.files or 'audio' not in request.files:
            return jsonify({'status': 'error', 'message': '未提供音频文件'}), 400
            
        feature_id = request.form.get('feature_id')
        group_id = request.form.get('group_id', config.default_group_id)
        audio_file = request.files['audio']
        
        if not feature_id:
            return jsonify({'status': 'error', 'message': '缺少用户ID'}), 400
            
        # 调用声纹验证方法
        result = voiceprint_service.verify_voiceprint(audio_file, feature_id, group_id)
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"声纹验证异常: {str(e)}")
        return jsonify({'status': 'error', 'message': str(e)}), 500


# 启动服务
if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5005)